/* Copyright(C) 2009, Swedish Institute of Computer Science */

%   File       : zinc_tests.pl
%   Author     : agren
%   Purpose    : Test file for library(zinc).

:- module(zinc_tests, [fzn_conformance/0,
		       fzn_g12/0,
		       fzn_interface/0,
		       mzn_g12/0,
		       mzn_interface/0,
		       mzn_to_fzn_all/1]).

:- use_module(library(clpfd)).
:- use_module(library(file_systems)).
:- use_module(library(lists)).
:- use_module(library(zinc)).

fzn_conformance :-
	absolute_file_name(library('zinc/tests/fcts'), FCTS),
	directories(FCTS).

directories(D) :-
	files(D),
	directory_member_of_directory(D, _, SubDir),
	directories(SubDir),
	fail.
directories(_).

files(D) :-
	file_member_of_directory(D, '*.fzn', FznBase, Fzn),
	atom_concat(FznBase0, '.fzn', FznBase),
	atom_concat(FznBase0, '.exp*', ExpPattern),
	file_members_of_directory(D, ExpPattern, ExpFiles0),
	keys_and_values(ExpFiles0, _, ExpFiles),
	open(temp('sptmp.res'), write, ResStream0,
	     [if_exists(generate_unique_name)]),
	stream_property(ResStream0, file_name(ResFile)),
	close(ResStream0),

	atom_concat(FznBase0, '.opt', OptBase),
	(   file_member_of_directory(D, OptBase, Opt)
	->  options(Opt, FznOptions0)
	;   FznOptions0 = []
	),
	FznOptions = [output(ResFile)|FznOptions0],	
	format(user_output, 'Running ~a ... ', [FznBase]),
	fzn_run_file(Fzn, FznOptions),
	format(user_output, 'checking result ... ', []),
	(   check_expected(ResFile, ExpFiles)
	->  format(user_output, 'OK!~n~n', [])
	;   format(user_error, '~n~n...ERROR: unexpected result of ~a~n~n', [Fzn])
	),
	fail.
files(_).

options(FznOpt, Options) :-
	open(FznOpt, read, Stream),
	parse_options(Stream, Lines),
	close(Stream),
	(   foreach(Line, Lines),
	    foreach(Option, Options)
	do  option(Line, Option)
	).

parse_options(Stream, Lines) :-
	read_line(Stream, Codes),
	(   Codes = end_of_file
	->  Lines = []
	;   atom_codes(Line, Codes),
	    Lines = [Line|Lines1],
	    parse_options(Stream, Lines1)
	).

option('--all', solutions(all)) :- !.
option(Option, _) :-
	format(user_error, '~n~n...ERROR: unexpected option ~a~n~n', [Option]),
	fail.

check_expected(ResFile, ExpFiles) :- !,
	member(ExpFile, ExpFiles),
	open(ResFile, read, ResStream),
	open(ExpFile, read, ExpStream),
	call_cleanup(compare_streams(ResStream, ExpStream),
		     (close(ResStream), close(ExpStream))).

stream_to_stream(S1, S2) :-
	get_code(S1, C),
	(   C = -1
	->  true
	;   put_code(S2, C),
	    stream_to_stream(S1, S2)
	).


compare_streams(S1, S2) :-
	get_code(S1, C1),
	get_code(S2, C2),
	(   C1 = C2
	->  (   C1 = -1
	    ->  true
	    ;   compare_streams(S1, S2)
	    )
	).

fzn_g12 :-
	file_member_of_directory(library('zinc/examples/G12'), '*.fzn', F0, F),
        format(user_error, 'Running test ~a ... ~n~n', [F0]),
	on_exception(E,
		     fzn_run_file(F, [statistics(true), timeout(60000)]),
		     (format(user_error, '~n~nERROR: got exception ~q~n~n', [E]),
		      fail)),
	format(user_error, '~n~nOK!~n~n', []),
        fail.
fzn_g12.

mzn_g12 :-
	file_member_of_directory(library('zinc/examples/G12'), '*.mzn', F0, F),
        format(user_error, 'Running test ~a ... ~n~n', [F0]),
	on_exception(E,
		     mzn_run_file(F, [statistics(true), timeout(60000)]),
		     (format(user_error, '~n~nERROR: got exception ~q~n~n', [E]),
		      fail)),
	format(user_error, '~n~nOK!~n~n', []),
        fail.
mzn_g12.

fzn_interface :-
	member(G, [fzn_run_file(library('zinc/examples/queen4')),
		   
		   fzn_run_file(library('zinc/examples/queen4'),
				[solutions(all), statistics(true)]),

		   fzn_run_file(library('zinc/examples/queen4'),
				[solutions(2)]),

		   fzn_load_file(library('zinc/examples/queen4'), _),

		   (open(library('zinc/examples/queen4.fzn'), read, Stream),
		    fzn_load_stream(Stream, _),
		    close(Stream)),

		   (open(library('zinc/examples/queen4.fzn'), read, Stream),
		    fzn_run_stream(Stream),
		    close(Stream)),

		   (open(library('zinc/examples/queen4.fzn'), read, Stream),
		    fzn_run_stream(Stream, [solutions(all)]),
		    close(Stream)),

		   (fzn_load_file(library('zinc/examples/queen4'), Queen4State),
		    fzn_post(Queen4State),
		    fzn_solve(Queen4State),
		    fzn_output(Queen4State)),

		   (fzn_load_file(library('zinc/examples/queen4'), Queen4State),
		    fzn_post(Queen4State),
		    fzn_identifier(Queen4State, q, Q),
		    findall(_, (labeling([], Q), fzn_output(Queen4State)), _)),
		   
		   (fzn_load_file(library('zinc/examples/queen4'), Queen4State),
		    fzn_post(Queen4State),
		    fzn_identifier(Queen4State, q, Q),
		    Q = [Q1, Q2|_],
		    Q1 #< Q2,
		    findall(_, (labeling([], Q), fzn_output(Queen4State)), _)),
		   
		   (fzn_load_file(library('zinc/examples/queen4'), Queen4State),
		    fzn_dump(Queen4State, [variables([q=Q])], queen4),
		    delete_file('queen4.pl'))
		  ]),
	write(user_output, G), nl, nl,
	call(G), nl, nl,
	fail.
fzn_interface.

mzn_interface :-
	member(G, [(mzn_load_file(library('zinc/examples/G12/golomb'), [],
				  GolombState),
		    fzn_objective(GolombState, O),
		    fzn_identifier(GolombState, mark, Mark),
		    labeling([minimize(O)], Mark),
		    fzn_output(GolombState)),

		   (NQueens = ["int: n;", 
			       "array [1..n] of var 1..n: q;", 
			       "constraint forall (i in 1..n, j in i+1..n)", 
			       "(q[i] != q[j] /\\", 
			       "q[i] + i != q[j] + j /\\", 
			       "q[i] - i != q[j] - j);", 
			       "solve satisfy;", 
			       "output [\"A solution to the \", show(n),", 
			       "\" Queens problem: \", show(q), \"\\n\"];"],
		   mzn_run_model(NQueens, [parameters([n=4])])),

		   (NQueens = ["int: n;", 
			       "array [1..n] of var 1..n: q;", 
			       "constraint forall (i in 1..n, j in i+1..n)", 
			       "(q[i] != q[j] /\\", 
			       "q[i] + i != q[j] + j /\\", 
			       "q[i] - i != q[j] - j);", 
			       "solve satisfy;", 
			       "output [\"A solution to the \", show(n),", 
			       "\" Queens problem: \", show(q), \"\\n\"];"],
		   mzn_run_model(NQueens,
				 [data_file(library('zinc/examples/queen4.dat'))])),
		   
		   (NQueens = ["int: n=4;", 
			       "array [1..n] of var 1..n: q;", 
			       "constraint forall (i in 1..n, j in i+1..n)", 
			       "(q[i] != q[j] /\\", 
			       "q[i] + i != q[j] + j /\\", 
			       "q[i] - i != q[j] - j);", 
			       "solve satisfy;", 
			       "output [\"A solution to the \", show(n),", 
			       "\" Queens problem: \", show(q), \"\\n\"];"],
		    mzn_run_model(NQueens)),

		   (NQueens = ["int: n0; int: n;", 
			       "array [1..n] of var 1..n: q;", 
			       "constraint forall (i in 1..n, j in i+1..n)", 
			       "(q[i] != q[j] /\\", 
			       "q[i] + i != q[j] + j /\\", 
			       "q[i] - i != q[j] - j);", 
			       "solve satisfy;", 
			       "output [\"A solution to the \", show(n),", 
			       "\" Queens problem: \", show(q), \"\\n\"];"],
		   mzn_load_model(NQueens, [parameters([n0=4, n=n0])], _)),

		   mzn_run_file(library('zinc/examples/queen'),
				[data_file(library('zinc/examples/queen4.dat'))]),

		   mzn_run_file(library('zinc/examples/queen'),
				[solutions(all),
				 data_file(library('zinc/examples/queen4.dat'))]),

		   mzn_run_file(library('zinc/examples/G12/queen_cp2')),

		   mzn_load_file(library('zinc/examples/queen'),
				 [parameters([n=4])], Queen4State),

		   (mzn_load_file(library('zinc/examples/queen'),
				  [parameters([n=4]), variables([q=Q])],
				  Queen4State),
		    Q = [Q1, Q2|_],
		    Q1 #< Q2,
		    findall(_, (labeling([], Q), fzn_output(Queen4State)), _)),

		   (mzn_load_file(library('zinc/examples/queen'),
				  [parameters([n=2]), variables([q=Q]),
				   post(false)], Queen4State),
		    findall(_, (labeling([], Q), fzn_output(Queen4State)), _)),

		   (mzn_to_fzn(library('zinc/examples/queen'),
			       [parameters([n=4])],
			       queen4),
		    fzn_run_file(queen4),
                    delete_file('queen4.fzn'))
		  ]),
	write(user_output, G), nl, nl,
	call(G), nl, nl,
	fail.
mzn_interface.

mzn_to_fzn_all(D) :-
	file_members_of_directory(D, '*.mzn', MznFiles0),
	keys_and_values(MznFiles0, _, MznFiles),
	(   foreach(MznFile, MznFiles)
	do  atom_concat(Base, '.mzn', MznFile),
	    atom_concat(Base, '.fzn', FznFile),
	    mzn_to_fzn(MznFile, FznFile)
	).
		       
